#include <stdio.h>
#include <sokol_app.h>
#include <sokol_log.h>
#include <sokol_time.h>

#include "../pacman/interface.h"
#include "renderer.h"
#include "audio.h"

static double get_time(void) {
    return stm_sec(stm_now());
}

static void pacman_audio_callback(float* samples, size_t buffer_size, void* userdata) {
    AudioDriver* driver = (AudioDriver*) userdata;

    for (int i = 0; i < buffer_size; i++)
        samples[i] *= 0.5f;

    audio_queue(driver, samples, buffer_size);
}

struct app_state
{
    GameInterface pacman;
    GameRenderer renderer;
    AudioDriver audio;

    double next_frame;

    int right_pressed;
    int left_pressed;
    int up_pressed;
    int down_pressed;
    int debug_pressed;
    int turbo_pressed;
} app;

void init(void) {
    stm_setup();

    // initialize audio backend
    if (audio_init(&app.audio)) {
        fprintf(stderr, "could not initialize audio driver\n");
        sapp_quit();
    }

    // initialize game state
    pacman_interface_init(&app.pacman);
    pacman_interface_setup_sound(&app.pacman, app.audio.sample_rate, pacman_audio_callback, &app.audio);

    // create game renderer
    if (game_renderer_init(&app.renderer, &app.pacman)) {
        sapp_quit();
    }

    app.next_frame = get_time();
}

void eventcb(const sapp_event* event) {
    switch (event->type) {
        case SAPP_EVENTTYPE_KEY_DOWN: {
            if (event->key_code == SAPP_KEYCODE_RIGHT || event->key_code == SAPP_KEYCODE_D)
                app.right_pressed = true;

            else if (event->key_code == SAPP_KEYCODE_DOWN || event->key_code == SAPP_KEYCODE_S)
                app.down_pressed = true;

            else if (event->key_code == SAPP_KEYCODE_LEFT || event->key_code == SAPP_KEYCODE_A)
                app.left_pressed = true;

            else if (event->key_code == SAPP_KEYCODE_UP || event->key_code == SAPP_KEYCODE_W)
                app.up_pressed = true;

            else if (event->key_code == SAPP_KEYCODE_F1)
                app.debug_pressed = true;

            else if (event->key_code == SAPP_KEYCODE_F1)
                app.turbo_pressed = true;

            break;
        }
        
        case SAPP_EVENTTYPE_KEY_UP: {
            if (event->key_code == SAPP_KEYCODE_RIGHT || event->key_code == SAPP_KEYCODE_D)
                app.right_pressed = false;

            else if (event->key_code == SAPP_KEYCODE_DOWN || event->key_code == SAPP_KEYCODE_S)
                app.down_pressed = false;

            else if (event->key_code == SAPP_KEYCODE_LEFT || event->key_code == SAPP_KEYCODE_A)
                app.left_pressed = false;

            else if (event->key_code == SAPP_KEYCODE_UP || event->key_code == SAPP_KEYCODE_W)
                app.up_pressed = false;

            else if (event->key_code == SAPP_KEYCODE_F1)
                app.debug_pressed = false;

            else if (event->key_code == SAPP_KEYCODE_F2)
                app.turbo_pressed = false;

            break;
        }

        default: break;
    }
}

void frame(void) {
    uint8_t inputs = 0;
    if (app.right_pressed)     inputs |= INPUT_RIGHT;
    if (app.up_pressed)        inputs |= INPUT_UP;
    if (app.left_pressed)      inputs |= INPUT_LEFT;
    if (app.down_pressed)      inputs |= INPUT_DOWN;
    if (app.debug_pressed)      inputs |= INPUT_DEBUG;

    pacman_interface_set_inputs(&app.pacman, inputs);

    double now_time = get_time();
    const double frame_len = 1.0 / 60.0;

    int i = 0;
    while (now_time >= app.next_frame) {
        pacman_interface_update(&app.pacman);

        // f2 makes the game run four time as fast
        if (app.turbo_pressed)
        {
            pacman_interface_update(&app.pacman);
            pacman_interface_update(&app.pacman);
            pacman_interface_update(&app.pacman);
        }
        
        app.next_frame += frame_len;
        if (++i > 8) {
            app.next_frame += frame_len * (int)((now_time - app.next_frame) / frame_len);
        }
    }

    app.renderer.view_width = sapp_width();
    app.renderer.view_height = sapp_height();
    game_renderer_draw(&app.renderer, pacman_interface_get_display_info(&app.pacman));
}

void cleanup(void) {
    pacman_interface_destroy(&app.pacman);
    audio_destroy(&app.audio);
    game_renderer_free(&app.renderer);
}

sapp_desc sokol_main(int argc, char* argv[]) {
    (void)argc;
    (void)argv;

    const int WINDOW_WIDTH = 224 * 2;
    const int WINDOW_HEIGHT = 288 * 2;

    return (sapp_desc) {
        .init_cb = init,
        .frame_cb = frame,
        .cleanup_cb = cleanup,
        .event_cb = eventcb,
        .width = WINDOW_WIDTH + 10,
        .height = WINDOW_HEIGHT + 10,
        .window_title = "PAC-MAN",
        .logger.func = slog_func,
        .high_dpi = true,
        .swap_interval = 1
    };
}